﻿
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using static LightCAD.MathLib.Constants;

namespace LightCAD.Three
{
    public static partial class THREE
    {
        public class ObjectLoader : Loader<Object3D>
        {
            public ObjectLoader(LoadingManager manager = null) : base(manager)
            {

            }
            public override Object3D load(string url, Action<Object3D> onLoad, onProcessDelegate onProgress = null, onErrorDelegate onError = null)
            {
                var scope = this;

                var path = (this.path == "") ? LoaderUtils.extractUrlBase(url) : this.path;
                this.resourcePath = this.resourcePath ?? path;
                Object3D result = null;
                var loader = new FileLoader(this.manager);
                loader.setPath(this.path);
                loader.setRequestHeader(this.requestHeader);
                loader.setWithCredentials(this.withCredentials);
                loader.load(url, (textObj) =>
                {
                    var text = (string)textObj;
                    //JObject json = null;
                    try
                    {

                      //  json = JObject.Parse(text);

                    }
                    catch (Exception error)
                    {

                        if (onError != null) onError(new ErrorEvent() { exception = error });

                        console.error("THREE:ObjectLoader: Can\"t parse " + url + ".", error.Message);

                        return;

                    }

                    //var metadata = json.GetValue("metadata") as JObject;

                    //if (metadata == null || metadata.GetValue("type") == null || metadata.GetValue("type").ToString().toLowerCase() == "geometry")
                    //{

                    //    if (onError != null)
                    //        onError(new ErrorEvent { exception = new Error("THREE.ObjectLoader: Can\"t load " + url) });

                    //    console.error("THREE.ObjectLoader: Can\"t load " + url);
                    //    return;

                    //}

                    //result = scope.parse(json, onLoad);

                }, onProgress, onError);
                return result;
            }
            //Object3D parse(JObject json, Action<Object3D> onLoad)
            //{

            //    //var animations = this.parseAnimations(json.animations);
            //    var shapes = this.parseShapes(json.GetValue("shapes") as JArray);
            //    var geometries = this.parseGeometries(json.GetArray("geometries"), shapes);
            //    Object3D _object = null;
            //    var images = this.parseImages(json.GetArray("images"), () =>
            //    {

            //        if (onLoad != null) onLoad(_object);

            //    });

            //    var textures = this.parseTextures(json.GetArray("textures"), images);
            //    //var materials = this.parseMaterials(json.materials, textures);

            //    _object = this.parseObject(json.GetValue("object") as JObject, geometries, materials, textures, animations);
            //    //var skeletons = this.parseSkeletons(json.skeletons, object);

            //    //var.bindSkeletons(_object, skeletons);

            //    //

            //    if (onLoad != null)
            //    {

            //        var hasImages = false;

            //        foreach (var item in images)
            //        {
            //            var uuid = item.Key;
            //            if (images[uuid].data is Image)
            //            {

            //                hasImages = true;
            //                break;

            //            }

            //        }

            //        if (hasImages == false) onLoad(_object);

            //    }

            //    return _object;
            //    //return null;
            //}


            //JsObj<Shape> parseShapes(JArray json)
            //{
            //    var shapes = new JsObj<Shape>();

            //    if (json != null)
            //    {
            //        for (int i = 0, l = json.Count; i < l; i++)
            //        {
            //            var shape = new Shape().fromJSON(json[i] as JObject) as Shape;
            //            shapes[shape.uuid] = shape;
            //        }
            //    }
            //    return shapes;
            //}

            //JsObj<Texture> parseTextures(JArray json, JsObj<Source> images)
            //{

            //    int parseConstant(JToken value, JsObj<int> type)
            //    {
            //        if (value.Type == JTokenType.Integer) return (int)value;
            //        console.warn("THREE.ObjectLoader.parseTexture: Constant should be in numeric form.", value);
            //        return type[value.ToString()];
            //    }

            //    var textures = new JsObj<Texture>();

            //    if (json != null)
            //    {

            //        for (int i = 0, l = json.Count; i < l; i++)
            //        {

            //            var data = json[i] as JObject;

            //            if (data.GetValue("image") == null)
            //            {

            //                console.warn("THREE.ObjectLoader: No \"image\" specified for", data.GetString("uuid"));

            //            }

            //            if (images[data.GetString("image")] == null)
            //            {

            //                console.warn("THREE.ObjectLoader: Undefined image", data.GetString("image"));

            //            }

            //            var source = images[data.GetString("image")];
            //            var image = source.data;

            //            Texture texture;

            //            if (image != null && image.Count > 0)
            //            {

            //                texture = new CubeTexture();

            //                if (image.length == 6) texture.needsUpdate = true;

            //            }
            //            else
            //            {

            //                if (image != null && image[0].GetField("data") != null)
            //                {

            //                    texture = new DataTexture();

            //                }
            //                else
            //                {

            //                    texture = new Texture();

            //                }

            //                if (image != null) texture.needsUpdate = true; // textures can have null image data

            //            }

            //            texture.source = source;

            //            texture.uuid = data.GetString("uuid");

            //            if (data.GetString("name") != null) texture.name = data.GetString("name");

            //            if (data.GetValue("mapping") != null) texture.mapping = parseConstant(data.GetValue("mapping"), TEXTURE_MAPPING);

            //            var offset = data.GetDoubleArray("offset");
            //            if (offset != null) texture.offset.fromArray(offset);

            //            var repeat = data.GetDoubleArray("repeat");
            //            if (repeat != null) texture.repeat.fromArray(repeat);

            //            var center = data.GetDoubleArray("center");
            //            if (center != null) texture.center.fromArray(center);

            //            var rotation = data.GetDouble("rotation");
            //            texture.rotation = rotation;

            //            var wrap = data.GetIntArray("wrap");
            //            if (wrap != null)
            //            {
            //                texture.wrapS = parseConstant(wrap[0], TEXTURE_WRAPPING);
            //                texture.wrapT = parseConstant(wrap[1], TEXTURE_WRAPPING);
            //            }

            //            if (data.GetValue("format") != null) texture.format = data.GetInt("format");
            //            if (data.GetValue("type") != null) texture.type = data.GetInt("type");
            //            if (data.GetValue("encoding") != null) texture.encoding = data.GetInt("encoding");

            //            if (data.GetValue("minFilter") != null) texture.minFilter = parseConstant(data.GetValue("minFilter"), TEXTURE_FILTER);
            //            if (data.GetValue("magFilter") != null) texture.magFilter = parseConstant(data.GetValue("magFilter"), TEXTURE_FILTER);
            //            if (data.GetValue("anisotropy") != null) texture.anisotropy = data.Value<int>("anisotropy");

            //            if (data.GetValue("flipY") != null) texture.flipY = data.Value<bool>("flipY");

            //            if (data.GetValue("generateMipmaps") != null) texture.generateMipmaps = data.Value<bool>("generateMipmaps");
            //            if (data.GetValue("premultiplyAlpha") != null) texture.premultiplyAlpha = data.Value<bool>("premultiplyAlpha");
            //            if (data.GetValue("unpackAlignment") != null) texture.unpackAlignment = data.GetInt("unpackAlignment");

            //            if (data.GetValue("userData") != null) texture.userData = data.Value<JsObj<object>>("userData");

            //            textures[data.GetString("uuid")] = texture;

            //        }

            //    }

            //    return textures;

            //}

            //JsObj<BufferGeometry> parseGeometries(JArray json, JsObj<Shape> shapes)
            //{
            //    var geometries = new JsObj<BufferGeometry>();
            //    if (json != null)
            //    {

            //        var bufferGeometryLoader = new BufferGeometryLoader();

            //        for (int i = 0, l = json.Count; i < l; i++)
            //        {

            //            BufferGeometry geometry = null;
            //            var data = json[i] as JObject;
            //            var dType = data.GetString("type");
            //            switch (dType)
            //            {
            //                case "BufferGeometry":
            //                case "InstancedBufferGeometry":
            //                    geometry = bufferGeometryLoader.parse(data);
            //                    break;
            //                default:
            //                    if (dType == "BoxGeometry")
            //                    {
            //                        geometry = null;//new BoxGeometry().fromJSON(data, shapes);
            //                    }
            //                    else
            //                    {
            //                        console.warn($"THREE.ObjectLoader: Unsupported geometry type {dType}");
            //                    }
            //                    break;
            //            }
            //            geometry.uuid = data.GetString("uuid");
            //            if (data.GetString("name") != null) geometry.name = data.GetString("name");
            //            if (geometry != null && data.GetValue("userData") != null)
            //                geometry.userData = data.GetJsObj("userData");
            //            geometries[data.GetString("uuid")] = geometry;
            //        }
            //    }
            //    return geometries;
            //}

            //JsObj<Material> parseMaterials(JArray json, JsObj<Texture> texutes) 
            //{
            //    const cache = { }; // MultiMaterial
            //    const materials = { };

            //    if (json != null)
            //    {

            //        var loader = new MaterialLoader();
            //        loader.setTextures(textures);

            //        for (let i = 0, l = json.length; i < l; i++)
            //        {

            //            const data = json[i];

            //            if (cache[data.uuid] === null)
            //            {

            //                cache[data.uuid] = loader.parse(data);

            //            }

            //            materials[data.uuid] = cache[data.uuid];

            //        }
            //    }

            //    return materials;
            //}

            //JsObj<Source> parseImages(JArray json, Action onLoad)
            //{

            //    var scope = this;
            //    var images = new JsObj<Source>();

            //    ImageLoader loader;

            //    Image loadImage(string url)
            //    {
            //        scope.manager.itemStart(url);
            //        return loader.load(url, (img) =>
            //        {
            //            scope.manager.itemEnd(url);
            //        }, null, (err) =>
            //        {
            //            scope.manager.itemError(url);
            //            scope.manager.itemEnd(url);
            //        });
            //    }

            //    Object deserializeImage(object image)
            //    {
            //        if (image is string)
            //        {
            //            var url = image.ToString();
            //            var regex = new Regex(@"^(\/\/)|([a-z]+:(\/\/)?)", RegexOptions.IgnoreCase);
            //            var path = regex.Match(url) != null ? url : scope.resourcePath + url;
            //            return loadImage(path);
            //        }
            //        else
            //        {
            //            if (image.GetFieldOrProperty("data") != null)
            //            {
            //                return new
            //                {
            //                    data = getTypedArray(image.type, image.data),
            //                    width = image.width,
            //                    height = image.height
            //                };
            //            }
            //            else
            //            {
            //                return null;
            //            }

            //        }

            //    }

            //    if (json != null && json.Count > 0)
            //    {
            //        var manager = new LoadingManager(onLoad);
            //        loader = new ImageLoader(manager);
            //        loader.setCrossOrigin(this.crossOrigin);
            //        for (int i = 0, il = json.Count; i < il; i++)
            //        {
            //            var image = json[i] as JObject;
            //            var url = image.GetValue("url");
            //            if (!(url is JArray))
            //            {
            //                // load array of images e.g CubeTexture
            //                var urlArr = url as JArray;
            //                var imageArray = new JsArr<IImage>();
            //                for (int j = 0, jl = urlArr.Count; j < jl; j++)
            //                {
            //                    var currentUrl = urlArr[j];
            //                    var deserializedImage = deserializeImage(currentUrl);
            //                    if (deserializedImage != null)
            //                    {
            //                        if (deserializedImage is Image)
            //                        {
            //                            imageArray.push(deserializedImage as Image);
            //                        }
            //                        else
            //                        {
            //                            // special case: handle array of data textures for cube textures
            //                            var data = deserializedImage.GetField("data") as Array;
            //                            var width = (int)deserializedImage.GetField("width");
            //                            var height = (int)deserializedImage.GetField("height");
            //                            imageArray.push(new DataTexture(data, width, height));
            //                        }
            //                    }
            //                }
            //                images[image.GetString("uuid")] = new Source(imageArray.ToArray());
            //            }
            //            else
            //            {
            //                // load single image
            //                var deserializedImage = deserializeImage(image.GetString("url")) as Image;
            //                images[image.GetString("uuid")] = new Source(deserializedImage);
            //            }
            //        }
            //    }
            //    return images;
            //}

            //Object3D parseObject(JObject data,JsObj<BufferGeometry> geometries,JsObj<Material> materials,JsObj<Texture> textures,JsObj<Animation> animations )
            //{
            //    Object3D _object=null;

            //    BufferGeometry getGeometry(string name )
            //    {

            //        if (geometries[name] == null)
            //        {

            //            console.warn("THREE.ObjectLoader: Undefined geometry", name);
            //        }

            //        return geometries[name];
            //    }

            //    object getMaterial(object name )
            //    {

            //        if (name == null) return null;
            //        if (name is IList<string>)
            //        {

            //            var array = new JsArr<Material>();

            //            for (int i = 0, l = (name as IList<string>).Count; i < l; i++)
            //            {

            //                var uuid = (name as IList<string>)[i];

            //                if (materials[uuid] == null)
            //                {

            //                    console.warn("THREE.ObjectLoader: Undefined material", uuid);

            //                }

            //                array.push(materials[uuid]);
            //            }

            //            return array;
            //        }

            //        if (materials[(string)name] == null)
            //        {

            //            console.warn("THREE.ObjectLoader: Undefined material", name);

            //        }

            //        return materials[(string)name];

            //    }

            //    Texture getTexture(string uuid )
            //    {

            //        if (textures[uuid] == null)
            //        {

            //            console.warn("THREE.ObjectLoader: Undefined texture", uuid);

            //        }

            //        return textures[uuid];

            //    }

            //    BufferGeometry geometry;
            //    Material material;
            //    geometry = getGeometry(data.GetString("geometry"));
            //    material = getMaterial(data.GetValue("material")) as Material;

            //    _object = new Mesh(geometry, material);

            //    _object.uuid = data.GetString("uuid");

            //    if (data.GetString("name") !=null) _object.name = data.name;

            //    if (data.GetValue("matrix") != null)
            //    {

            //        _object.matrix.fromArray(data.GetDoubleArray("matrix"));

            //        if (data.matrixAutoUpdate != null) _object.matrixAutoUpdate = data.matrixAutoUpdate;
            //        if (_object.matrixAutoUpdate) _object.matrix.decompose(_object.position, _object.quaternion, _object.scale);

            //    }
            //    else
            //    {

            //        if (data.position != null) _object.position.fromArray(data.position);
            //        if (data.rotation != null) _object.rotation.fromArray(data.rotation);
            //        if (data.quaternion != null) _object.quaternion.fromArray(data.quaternion);
            //        if (data.scale != null) _object.scale.fromArray(data.scale);

            //    }

            //    if (data.castShadow != null) _object.castShadow = data.castShadow;
            //    if (data.receiveShadow != null) _object.receiveShadow = data.receiveShadow;

            //    if (data.shadow)
            //    {

            //        if (data.shadow.bias != null) _object.shadow.bias = data.shadow.bias;
            //        if (data.shadow.normalBias != null) _object.shadow.normalBias = data.shadow.normalBias;
            //        if (data.shadow.radius != null) _object.shadow.radius = data.shadow.radius;
            //        if (data.shadow.mapSize != null) _object.shadow.mapSize.fromArray(data.shadow.mapSize);
            //        if (data.shadow.camera != null) _object.shadow.camera = this.parseObject(data.shadow.camera);

            //    }

            //    if (data.visible != null) _object.visible = data.visible;
            //    if (data.frustumCulled != null) _object.frustumCulled = data.frustumCulled;
            //    if (data.renderOrder != null) _object.renderOrder = data.renderOrder;
            //    if (data.userData != null) _object.userData = data.userData;
            //    if (data.layers != null) _object.layers.mask = data.layers;

            //    if (data.children != null)
            //    {

            //        const children = data.children;

            //        for (let i = 0; i < children.length; i++)
            //        {

            //            _object.add(this.parseObject(children[i], geometries, materials, textures, animations));

            //        }

            //    }

            //    if (data.animations != null)
            //    {

            //        const objectAnimations = data.animations;

            //        for (let i = 0; i < objectAnimations.length; i++)
            //        {

            //            const uuid = objectAnimations[i];

            //            _object.animations.push(animations[uuid]);

            //        }

            //    }

            //    if (data.type === "LOD")
            //    {

            //        if (data.autoUpdate != null) _object.autoUpdate = data.autoUpdate;

            //        const levels = data.levels;

            //        for (let l = 0; l < levels.length; l++)
            //        {

            //            const level = levels[l];
            //            const child = _object.getObjectByProperty("uuid", level.object);

            //            if (child != null)
            //            {

            //                _object.addLevel(child, level.distance, level.hysteresis);

            //            }

            //        }

            //    }

            //    return object;

            //}

            public static JsObj<int> TEXTURE_MAPPING = new JsObj<int>
            {
                { "UVMapping", UVMapping},
                {"CubeReflectionMapping", CubeReflectionMapping},
                {"CubeRefractionMapping", CubeRefractionMapping},
                {"EquirectangularReflectionMapping", EquirectangularReflectionMapping},
                {"EquirectangularRefractionMapping", EquirectangularRefractionMapping},
                {"CubeUVReflectionMapping", CubeUVReflectionMapping}
            };
            public static JsObj<int> TEXTURE_WRAPPING = new JsObj<int>
            {
                { "RepeatWrapping", RepeatWrapping },
                {"ClampToEdgeWrapping", ClampToEdgeWrapping},
                {"MirroredRepeatWrapping", MirroredRepeatWrapping }
            };

            public static JsObj<int> TEXTURE_FILTER = new JsObj<int>
            {
                { "NearestFilter", NearestFilter },
                {"NearestMipmapNearestFilter", NearestMipmapNearestFilter},
                {"NearestMipmapLinearFilter", NearestMipmapLinearFilter},
                {"LinearFilter", LinearFilter},
                {"LinearMipmapNearestFilter", LinearMipmapNearestFilter},
                {"LinearMipmapLinearFilter", LinearMipmapLinearFilter}
            };
        }
    }
}
